/*
 * Copyright (c) 2010-2011 Josef Hardi <josef.hardi@gmail.com>
 * Copyright (c) 2010 Piotr Zagawa <piotr.zagawa@gmail.com>
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package id.duapuluhdua.dakidaki.gps;

import java.util.Observable;

import android.content.Intent;
import android.location.Location;
import android.os.Handler;
import android.util.Log;

/**
 *
 */
public class GpsLocationStatus extends Observable
{
  /** Constants */
  private static final String TAG = "GpsLocationStatus";

  public final static Integer STATE_NONE = 0;
  public final static Integer STATE_NEW_GPS_LOCATION = 1;

  private final Boolean bIntentAccessMutex = false;
  private final Boolean bLocationAccessMutex = false;

  /** Fields */
  private Intent mIntentToProcess;  // shared intent to process
  private Location mLocationToProcess;  // shared location to process

  private GpsLocation mLocCurrent;   // from GPS
  private GpsLocation mLocLastSaved; // cache from database

  private Handler mHandler;

  /**
   * The default constructor.
   */
  public GpsLocationStatus()
  {
    mLocCurrent = new GpsLocation();
    mLocLastSaved = new GpsLocation();

    mHandler = new Handler();
  }

  /**
   * Receives an Intent operation from a broadcast data receiver and then
   * records the current Intent data. If the GPSLocation is in update or add
   * mode then records the current location data to a cache.
   *
   * @param intent The intent operation from a broadcast data receiver.
   */
  public void postIntent(Intent intent)
  {
    synchronized(bIntentAccessMutex)
    {
      mIntentToProcess = new Intent(intent);
      Thread t = new Thread()  //run thread
      {
        @Override
        public void run()
        {
          set(mIntentToProcess);
          mIntentToProcess = null;
        }
      };
      t.start();
    }
  }

  /** Records the location to a cache (locLastSaved). */
  private void set(Intent intent)
  {
    synchronized(mLocCurrent)
    {
      if (mLocCurrent.get(intent)) {
        if (isSaveMode()) { // store last saved location
          synchronized(mLocLastSaved)
          {
            mLocLastSaved.set(mLocCurrent);
          }
        }
        // Notify observers of location change
        mHandler.removeCallbacks(mUpdateObserversTask);
        mHandler.post(mUpdateObserversTask);
      }
    }
  }

  /**
   * Receives a new location from the GPS reader and then records this new
   * information to the global position object. If the recording is
   * successful then notifies the observers by queuing the message.
   *
   * @param location The new location object.
   */
  public void postLocation(Location location)
  {
    if (location == null) {
      return;
    }

    synchronized(bLocationAccessMutex)
    {
      mLocationToProcess = new Location(location);
      Thread t = new Thread()
      {
        @Override
        public void run()
        {
          set(mLocationToProcess);
          mLocationToProcess = null;
        }
      };
      t.start();
    }
  }

  // Records the new location to the global position object (locCurrent).
  private void set(Location location)
  {
    if (location == null) {
      return;
    }

    synchronized(mLocCurrent)
    {
      if (mLocCurrent.set(location, -1, GpsLogger.MODE_NONE)) {
        //notify observers of location change
        mHandler.removeCallbacks(mUpdateObserversTask);
        mHandler.post(mUpdateObserversTask);
      }
    }
  }

  // A runnable thread to be added to the message queue.
  private Runnable mUpdateObserversTask = new Runnable() {
    @Override
    public void run()
    {
      try {
        GpsLocationStatus.this.setChanged();
        GpsLocationStatus.this.notifyObservers(STATE_NEW_GPS_LOCATION);
      }
      catch (Exception e) {
        Log.e(TAG, e.getMessage());
      }
    }
  };

  /**
   * Get the GPS accuracy of the current location.
   *
   * @return the accuracy measure.
   */
  public int getAccuracy()
  {
    int iValue = 0;
    synchronized(mLocCurrent)
    {
      iValue = mLocCurrent.accuracy;
    }
    return iValue;
  }

  /**
   * Copy the current location to out.
   *
   * @param out the copy holder.
   */
  public void copyCurrent(GpsLocation out)
  {
    synchronized(mLocCurrent)
    {
      out.set(mLocCurrent);
    }
  }

  /**
   * Copy the last saved location to out.
   *
   * @param out the copy holder.
   */
  public void copyLastSaved(GpsLocation out)
  {
    synchronized(mLocLastSaved)
    {
      out.set(mLocLastSaved);
    }
  }

  /**
   * Returns the saving mode of the GPSLocation object.
   *
   * @return true if the GPS location is in add or update mode, or false
   * otherwise.
   */
  public boolean isSaveMode()
  {
    boolean bSaveMode = false;
    synchronized(mLocCurrent)
    {
      bSaveMode = (mLocCurrent.saveMode != GpsLogger.MODE_NONE);
    }
    return bSaveMode;
  }
}
